package arimaa;

import sun.audio.*;    //import the sun.audio package
import java.io.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;

public class Arimaa implements ActionListener, MouseListener, ComponentListener {

    public static final boolean GOLD = false;
    public static final boolean SILVER = true;
    //public static final Arraylist<ArrayList> list = new Arraylist<ArrayList>(oldList);
    //^^^^^ lolz
    //pieces in the board
    ArrayList<Piece> goldPieces = new ArrayList<Piece>();
    ArrayList<Piece> silverPieces = new ArrayList<Piece>();
    //pieces that have been captured
    ArrayList<Piece> capturedGoldPieces = new ArrayList<Piece>();
    ArrayList<Piece> capturedSilverPieces = new ArrayList<Piece>();
    //pieces that have yet to be placed
    ArrayList<Piece> storedGoldPieces = new ArrayList<Piece>();
    ArrayList<Piece> storedSilverPieces = new ArrayList<Piece>();
    ArrayList<String> boards = new ArrayList<String>();
    //PieceDisplayer is a window that will show all of the pieces that have yet
    //to be displayed at the beginning of the game, and will show all of the pieces
    //that have been captured after initial piece placement.
    PieceDisplayer goldPieceShower = new PieceDisplayer(storedGoldPieces);
    PieceDisplayer silverPieceShower = new PieceDisplayer(storedSilverPieces);
    int numberOfMoves = 4;
    boolean playerTurn = GOLD;
    Square[][] boardSquares = new Square[8][8];
    JFrame jf = new JFrame("Arimaa");
    JLabel moveLabel = new JLabel("Moves left: 4");
    JLabel currentPlayer = new JLabel("Current Player: Gold");
    JButton doneButton = new JButton("End turn early.");
    JButton resetButton = new JButton("Reset");
    Board board = new Board(boardSquares);
    Container south = new Container();
    Container east = new Container();
    Container west = new Container();
    JButton saveGame = new JButton("Save Game");
    JButton loadGame = new JButton("Load Game");
    File saveFile = new File("save.txt");
    File loadFile = new File("load.txt");
    // JLabel goldTitle = new JLabel("==GOLD PIECES==");
    // JLabel silverTitle = new JLabel("=SILVER PIECES=");
    //temp space for the currently selected piece (moving/placing).
    Piece selectedPiece;
    //macro states
    final int INITIAL = 0;
    final int PLAYING = 1;
    //micro statuses for INITIAL.
    //micro states
    final int UNSELECTED = 0;
    final int SELECTED = 1;
    int macroState = INITIAL;
    int microState = UNSELECTED;

    Arimaa() {
        resetBoard(true);

        Piece.loadImages();

        //camel of doom, adjust drawing in Board for (i+1)*...
        //boardSquares[4][4].setPiece(new Piece(4,GOLD));

        jf.setSize(900, 600);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //jf.addComponentListener(this);
        jf.setLayout(new BorderLayout());
        jf.add(board, BorderLayout.CENTER);
        jf.add(south, BorderLayout.SOUTH);
        jf.add(east, BorderLayout.EAST);
        jf.add(west, BorderLayout.WEST);
        south.setLayout(new GridLayout(1, 6));
        goldPieceShower.setPreferredSize(new Dimension(jf.getWidth() / 6, jf.getHeight()));
        silverPieceShower.setPreferredSize(new Dimension(jf.getWidth() / 6, jf.getHeight()));
        goldPieceShower.addComponentListener(this);
        silverPieceShower.addComponentListener(this);

        south.add(currentPlayer);
        south.add(moveLabel);

        south.add(doneButton);
        south.add(resetButton);
        south.add(saveGame);
        south.add(loadGame);
        saveGame.addActionListener(this);
        loadGame.addActionListener(this);
        resetButton.addActionListener(this);
        moveLabel.setOpaque(true);
        currentPlayer.setOpaque(true);
        resetButton.setOpaque(true);
        saveGame.setOpaque(true);
        loadGame.setOpaque(true);

        moveLabel.setForeground(Color.WHITE);
        currentPlayer.setForeground(Color.WHITE);
        doneButton.setForeground(Color.WHITE);
        resetButton.setForeground(Color.WHITE);
        loadGame.setForeground(Color.WHITE);
        saveGame.setForeground(Color.WHITE);

        moveLabel.setBackground(Color.getHSBColor(0.0f, 0.492f, 0.249f));
        currentPlayer.setBackground(Color.getHSBColor(0.0f, 0.492f, 0.249f));
        doneButton.setBackground(Color.getHSBColor(0.0f, 0.492f, 0.249f));
        resetButton.setBackground(Color.getHSBColor(0.0f, 0.492f, 0.249f));
        saveGame.setBackground(Color.getHSBColor(0.0f, 0.492f, 0.249f));
        loadGame.setBackground(Color.getHSBColor(0.0f, 0.492f, 0.249f));
        doneButton.addActionListener(this);
        east.setLayout(new GridLayout(1, 1));
        west.setLayout(new GridLayout(1, 1));

        east.add(goldPieceShower);
        west.add(silverPieceShower);
        board.addMouseListener(this);
        goldPieceShower.addMouseListener(this);
        silverPieceShower.addMouseListener(this);

        //setupShowers();

        jf.setVisible(true);
    }

    public static void main(String[] args) {
        Arimaa newinstance = new Arimaa();
    }

    public void resetBoard(boolean fuckThis) {
        for (int i = 0; i < boardSquares.length; i++) {
            for (int j = 0; j < boardSquares.length; j++) {
                boardSquares[i][j] = new Square();
                boardSquares[i][j].setX(i);
                boardSquares[i][j].setY(j);
                if ((i == 2 && j == 2) || (i == 2 && j == 5) || (i == 5 && j == 2) || (i == 5 && j == 5)) {
                    boardSquares[i][j].setTrap(true);
                }
            }
        }
        goldPieces.clear();
        silverPieces.clear();
        capturedGoldPieces.clear();
        capturedSilverPieces.clear();
        storedGoldPieces.clear();
        storedSilverPieces.clear();
        //setupShowers();//rub a dub dub
        if (fuckThis == true) {
            setupDisplayers();
        }
        board.updateSquares(boardSquares);
        boards.clear();
    }

    public void actionPerformed(ActionEvent ae) {
        if (ae.getSource().equals(resetButton)) {
            resetBoard(true);
            setupDisplayers();
            jf.repaint();
        } else if (ae.getSource().equals(doneButton)) {
            playerTurn = !playerTurn;
            numberOfMoves = 4;
            updateLabel();
        } else if (ae.getSource().equals(saveGame)) {
            chooseSaveFile();
            try {
                FileWriter fw = new FileWriter(saveFile);
                for (int i = 0; i < boards.size(); i++) {
                    fw.write(boards.get(i) + "\n");
                }
                fw.close();
            } catch (Exception e) {
            }
            System.out.println("Done.");
        } else if (ae.getSource().equals(loadGame)) {
            chooseLoadFile();
            resetBoard(false);

            if (loadFile.getName().equals("killerCamel.txt")) {
                boardSquares[4][4].setPiece(new Piece(4, GOLD));
                board.setCamelMode(true);

                //audio
                InputStream in = null;
                try {
                    in = new FileInputStream("sounds/camel.wav");
                } catch (FileNotFoundException ex) {
                    Logger.getLogger(Arimaa.class.getName()).log(Level.SEVERE, null, ex);
                }
                AudioStream as = null;
                try {
                    as = new AudioStream(in);
                } catch (IOException ex) {
                    Logger.getLogger(Arimaa.class.getName()).log(Level.SEVERE, null, ex);
                }
                AudioPlayer.player.start(as);
                try {
                    Thread.sleep(3600);
                } catch (Exception e) {
                }
                //end audio
                jf.repaint();
            } else {
                try {

                    BufferedReader br = new BufferedReader(new FileReader(loadFile));
                    String line = br.readLine();

                    while (line != null) {
                        boards.add(line);
                        line = br.readLine();
                    }

                } catch (Exception e) {
                    System.out.println("Invalid input.");
                }
                setupBoard();
            }
        }
    }

    public void mouseClicked(MouseEvent me) {
    }

    public void mousePressed(MouseEvent me) {
    }

    public void mouseReleased(MouseEvent me) {
        //if the click was in one of the PieceDisplayers and 
        //the status is initial piece placement.

        if (!me.getSource().equals(board)) {
            clickedOnDisplayer(me);
        }
        //if the board was clicked
        if (me.getSource().equals(board)) {
            //if the status is still in piece placement
            if (macroState == INITIAL) {
                clickedOnBoardInitial(me);
            } else { // _xXx_==.'.'.~/*fuck*/~.'.'.==_xXx_
                clickedOnBoardPlaying(me);
            }
        }
    }

    public void clickedOnDisplayer(MouseEvent me) {
        if (macroState == PLAYING) {
            return; //we dont do anything with it in the playing state
        }
        //deciding which PieceDisplayer was selected
        PieceDisplayer clicked = (PieceDisplayer) me.getSource();
        int x = me.getX() * 2 / clicked.getWidth();
        int y = me.getY() * 8 / clicked.getHeight();
        //position in the array list of pieces in PieceDisplayer
        int position = x * 8 + y;
        if (position < clicked.getPiecesLeft()) {
            //selecting that piece for placement
            unselect();
            selectedPiece = clicked.getPiece(position);
            selectedPiece.setSelected(true);
            microState = SELECTED;
        }
        jf.repaint();
    }

    public void select(MouseEvent me) {
        int x = me.getX() * 8 / board.getWidth();
        int y = me.getY() * 8 / board.getHeight();

        if (boardSquares[x][y].getPiece() == null) {
            //boardSquares[x][y].setHighlighted(true);
            /*
             * if (selectedPiece != null) { selectedPiece.setSelected(false);
             * selectedPiece = null; }
             */
        } else {

            selectedPiece = boardSquares[x][y].getPiece();
            if (selectedPiece.getPlayer() != playerTurn) {
                selectedPiece = null;
                return;
            }
            unselect();
            selectedPiece.setSelected(true);
            selectedPiece.setX(x);
            selectedPiece.setY(y);
        }
        jf.repaint();

    }

    public void clickedOnBoardInitial(MouseEvent me) {
        //if a piece has already been selected.
        if (selectedPiece != null) {

            int x = me.getX() * 8 / board.getWidth();
            int y = me.getY() * 8 / board.getHeight();
            boolean player = selectedPiece.getPlayer();
            //if the piece is gold
            //  System.out.println(macroState);
            if (player == GOLD) {
                //if the clicked square is withing gold's starting area
                if (boardSquares[x][y].getPiece() == null && y > 5) {
                    boardSquares[x][y].setPiece(selectedPiece);
                    //     selectedPiece.setX(x);
                    //      selectedPiece.setY(y);
                    goldPieces.add(selectedPiece);
                    //only one will actually be removed. the other one won't do anything.
                    goldPieceShower.remove(selectedPiece);
                    silverPieceShower.remove(selectedPiece);
                    selectedPiece.setSelected(false);
                    selectedPiece = null;
                    //    System.out.println("bobby");
                    jf.repaint();
                }
            } //if the piece was silver, then don't place it unless all of gold's pieces
            //have been placed. Otherwise, behave the same as for gold piece placement.
            else if (player == SILVER && goldPieceShower.getPiecesLeft() == 0) {
                if (boardSquares[x][y].getPiece() == null && y < 2) {
                    boardSquares[x][y].setPiece(selectedPiece);
                    //     selectedPiece.setX(x);
                    //     selectedPiece.setY(y);
                    goldPieces.add(selectedPiece);
                    goldPieceShower.remove(selectedPiece);
                    silverPieceShower.remove(selectedPiece);
                    selectedPiece.setSelected(false);
                    selectedPiece = null;
                    jf.repaint();
                    if (silverPieceShower.getPiecesLeft() == 0) {
                        macroState = PLAYING;
                        //       System.out.println("ohai");
                    }
                }
            }
        }
    }

    public void clickedOnBoardPlaying(MouseEvent me) {
        int x = me.getX() * 8 / board.getWidth();
        int y = me.getY() * 8 / board.getHeight();
        //select(me);
        //  freezePieces();
        //  boardOperations();
        if (selectedPiece != null) {
            //  System.out.println(isLegal(x, y));
            //  if (hasMoves() == true) {
            if (boardSquares[x][y].getPiece() == null) {
                if (isLegal(x, y)) {
                    boardSquares[selectedPiece.getX()][selectedPiece.getY()].getPiece().setSelected(false);
                    boardSquares[selectedPiece.getX()][selectedPiece.getY()].setPiece(null);
                    boardSquares[x][y].setPiece(selectedPiece);
                    selectedPiece = null;
                    numberOfMoves--;
                    updateLabel();
                    boardOperations();
                    jf.repaint();
                    if (victory() == 1) {
                        JOptionPane.showMessageDialog(jf, "The Gold player won!");
                        resetBoard(true);
                        setupDisplayers();
                        jf.repaint();
                    } else if (victory() == 2) {
                        JOptionPane.showMessageDialog(jf, "The Silver player won!");
                        resetBoard(true);
                        setupDisplayers();
                        jf.repaint();
                    } //no victory
                    else {
                        if (hasMoves() == false) {
                            playerTurn = !playerTurn;
                            numberOfMoves = 4;
                            updateLabel();
                            jf.repaint();
                        }
                    }

                }
            } else {
                select(me);
            }
            //  } else {
            //  playerTurn = !playerTurn;
            //  numberOfMoves = 4;
            // updateLabel();
            //  jf.repaint();
            // }
        } else {
            //   System.out.println("sout");
            select(me);
        }

    }

    public void mouseEntered(MouseEvent me) {
    }

    public void mouseExited(MouseEvent me) {
    }

    private void setupDisplayers() {
        storedSilverPieces.clear();
        storedGoldPieces.clear();
        for (int i = 0; i < 8; i++) {
            storedGoldPieces.add(new Piece(0, GOLD));
            storedSilverPieces.add(new Piece(0, SILVER));
        }
        for (int i = 1; i < 6; i++) {
            if (i < 4) {

                storedGoldPieces.add(new Piece(i, GOLD));
                storedGoldPieces.add(new Piece(i, GOLD));
                storedSilverPieces.add(new Piece(i, SILVER));
                storedSilverPieces.add(new Piece(i, SILVER));

            } else {
                storedGoldPieces.add(new Piece(i, GOLD));
                storedSilverPieces.add(new Piece(i, SILVER));
            }
        }

    }

    //sets all of the pieces and squares to be non-selected.
    public void unselect() {

        for (Piece piece : goldPieces) {
            piece.setSelected(false);
        }
        for (Piece piece : silverPieces) {
            piece.setSelected(false);
        }
        for (Piece piece : capturedGoldPieces) {
            piece.setSelected(false);
        }
        for (Piece piece : capturedSilverPieces) {
            piece.setSelected(false);
        }
        for (Piece piece : storedGoldPieces) {
            piece.setSelected(false);
        }
        for (Piece piece : storedSilverPieces) {
            piece.setSelected(false);
        }
        //now removing selection from all of the squares
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                boardSquares[i][j].setSelected(false);

            }

        }

    }

    public void componentResized(ComponentEvent e) {
        goldPieceShower.setPreferredSize(new Dimension(jf.getWidth() / 6, jf.getHeight()));
        silverPieceShower.setPreferredSize(new Dimension(jf.getWidth() / 6, jf.getHeight()));
    }

    public void componentMoved(ComponentEvent e) {
    }

    public void componentShown(ComponentEvent e) {
    }

    public void componentHidden(ComponentEvent e) {
    }

    public void freezePieces() {
        for (int i = 0; i < boardSquares.length; i++) {
            for (int j = 0; j < boardSquares.length; j++) {
                Piece current = boardSquares[i][j].getPiece();
                if (current != null) {

                    checkFriendlyAdjacent(current, i, j);
                    int rank = current.getRank();
                    boolean owner = current.getPlayer();
                    Piece[] adj = new Piece[4];
                    if (i != 0) {
                        adj[0] = boardSquares[i - 1][j].getPiece();
                    }
                    if (j != 0) {
                        adj[1] = boardSquares[i][j - 1].getPiece();
                    }
                    if (i != 7) {
                        adj[2] = boardSquares[i + 1][j].getPiece();
                    }
                    if (j != 7) {
                        adj[3] = boardSquares[i][j + 1].getPiece();
                    }

                    if (checkFriendlyAdjacent(current, i, j) == false) {
                        boolean higherRank = false;
                        for (int k = 0; k < adj.length; k++) {
                            if (adj[k] != null && adj[k].getRank() > rank) {
                                current.setFrozen(true);
                                higherRank = true;
                            }

                        }
                        if (higherRank == false) {
                            current.setFrozen(false);
                        }
                    } else {
                        current.setFrozen(false);
                    }
                }
            }
        }
    }

    private boolean isLegal(int x, int y) {

        if (isAdjacent(x, y) && isEmpty(x, y) && isNotFrozen(x, y) && hasMoves()) {
            boardSquares[x][y].setAllowed(true);
            return true;
        } else {
            return false;
        }
    }

    //the square must be adjacent to the current square
    public boolean isAdjacent(int x, int y) {

        if ((x == selectedPiece.getX() + 1 && y == selectedPiece.getY()) || (x == selectedPiece.getX() && y == selectedPiece.getY() - 1) || (x == selectedPiece.getX() - 1 && y == selectedPiece.getY()) || (x == selectedPiece.getX() && y == selectedPiece.getY() + 1)) {
            return true;
        } else {
            return false;
        }
    }

    //the square must be unoccupied
    public boolean isEmpty(int x, int y) {
        if (boardSquares[x][y].getPiece() == null) {
            return true;
        } else {
            return false;
        }
    }

    //the piece must not be frozen
    public boolean isNotFrozen(int x, int y) {
        if (selectedPiece.isFrozen() == false) {
            return true;
        } else {
            return false;
        }
    }

    //there must be enough moves left in the player's turn
    public boolean hasMoves() {
        if (numberOfMoves > 0) {
            return true;
        } else {
            return false;
        }
    }

    public int victory() {
        if (true) {
            return 0;
        }
        //return 0 if no victory, 1 if gold, 2 if silver
        for (int i = 0; i < boardSquares.length; i++) {
            if (boardSquares[i][0].getPiece() != null && boardSquares[i][0].getPiece().getRank() == 0 && boardSquares[i][0].getPiece().getPlayer() == SILVER) {
                return 2;
            } else if (boardSquares[i][7].getPiece() != null && boardSquares[i][7].getPiece().getRank() == 0 && boardSquares[i][8].getPiece().getPlayer() == GOLD) {
                return 1;
            }
        }
        boolean hasRabbits = false;
        for (int i = 0; i < silverPieces.size(); i++) {
            if (silverPieces.get(i).getRank() == 0) {
                hasRabbits = true;
            }

        }
        if (hasRabbits == false) {
            return 1;
        }
        hasRabbits = false;
        for (int i = 0; i < goldPieces.size(); i++) {
            if (goldPieces.get(i).getRank() == 0) {
                hasRabbits = true;
            }

        }
        if (hasRabbits == false) {
            return 2;
        }
        return 0;
    }

    public void updateLabel() {
        moveLabel.setText("Moves left: " + numberOfMoves);
        if (playerTurn == GOLD) {
            currentPlayer.setText("Current player: Gold");
        } else {
            currentPlayer.setText("Current player: Silver");
        }
        jf.repaint();
    }

    public void boardOperations() {
        capturePieces();
        freezePieces();
        boards.add(new String(buildStringFromBoard()));
        jf.repaint();
    }

    public void capturePieces() {
        if (boardSquares[2][2].getPiece() != null) {
            Piece current = boardSquares[2][2].getPiece();
            if (checkFriendlyAdjacent(current, 2, 2) == false) {
                boardSquares[2][2].setPiece(null);
                if (current.getPlayer() == GOLD) {
                    goldPieces.remove(current);
                    capturedGoldPieces.add(current);
                } else {
                    silverPieces.remove(current);
                    capturedSilverPieces.add(current);
                }
            }
        }
        if (boardSquares[2][5].getPiece() != null) {
            Piece current = boardSquares[2][5].getPiece();
            if (checkFriendlyAdjacent(current, 2, 5) == false) {
                boardSquares[2][5].setPiece(null);
                if (current.getPlayer() == GOLD) {
                    goldPieces.remove(current);
                    capturedGoldPieces.add(current);
                } else {
                    silverPieces.remove(current);
                    capturedSilverPieces.add(current);
                }
            }
        }
        if (boardSquares[5][2].getPiece() != null) {
            Piece current = boardSquares[5][2].getPiece();
            if (checkFriendlyAdjacent(current, 5, 2) == false) {
                boardSquares[5][2].setPiece(null);
                if (current.getPlayer() == GOLD) {
                    goldPieces.remove(current);
                    capturedGoldPieces.add(current);
                } else {
                    silverPieces.remove(current);
                    capturedSilverPieces.add(current);
                }
            }
        }
        if (boardSquares[5][5].getPiece() != null) {
            Piece current = boardSquares[5][5].getPiece();
            if (checkFriendlyAdjacent(current, 5, 5) == false) {
                boardSquares[5][5].setPiece(null);
                if (current.getPlayer() == GOLD) {
                    goldPieces.remove(current);
                    capturedGoldPieces.add(current);
                } else {
                    silverPieces.remove(current);
                    capturedSilverPieces.add(current);
                }
            }
        }
        jf.repaint();
    }

    public boolean checkFriendlyAdjacent(Piece current, int i, int j) {
        boolean owner = current.getPlayer();
        Piece[] adj = new Piece[4];
        if (i != 0) {
            adj[0] = boardSquares[i - 1][j].getPiece();
        }
        if (j != 0) {
            adj[1] = boardSquares[i][j - 1].getPiece();
        }
        if (i != 7) {
            adj[2] = boardSquares[i + 1][j].getPiece();
        }
        if (j != 7) {
            adj[3] = boardSquares[i][j + 1].getPiece();
        }
        // boolean isConnected = false;
        for (int k = 0; k < adj.length; k++) {
            if (adj[k] != null && adj[k].getPlayer() == owner) {
                return true;
            }


        }
        return false;
    }

    public String buildStringFromBoard() {
        String build = "";
        for (int i = 0; i < boardSquares.length; i++) {
            for (int j = 0; j < boardSquares.length; j++) {
                Piece current = boardSquares[i][j].getPiece();
                if (current == null) {
                    build += "NN";
                } else {
                    if (current.getPlayer() == GOLD) {
                        build += "G";
                    } else {
                        build += "S";
                    }
                    build += current.getRank() + "";
                }
            }
        }
        System.out.println(build);
        return build;
    }

    //uses JFileChooser to select a file to write to
    private void chooseSaveFile() {
        JFileChooser fileChooser = new JFileChooser();
        if (fileChooser.showSaveDialog(jf) == JFileChooser.APPROVE_OPTION) {
            saveFile = fileChooser.getSelectedFile();
        } else {
            return;
        }
    }

    //uses JFileChooser to select a file to write to
    private void chooseLoadFile() {
        JFileChooser fileChooser = new JFileChooser();
        if (fileChooser.showOpenDialog(jf) == JFileChooser.APPROVE_OPTION) {
            loadFile = fileChooser.getSelectedFile();
        } else {
            return;
        }
    }

    private void setupBoard() {
        {
            String last = boards.get(boards.size() - 1);
            for (int i = 0; i < last.length(); i += 2) {
                if (last.charAt(i) == 'N') {
                    //weeee
                } else {
                    int xpos = i / 16;
                    int ypos = (i % 16) / 2;
                    boolean owner;
                    int rank = Integer.parseInt(last.charAt(i + 1) + "");
                    if (last.charAt(i) == 'G') {
                        owner = GOLD;
                    } else {
                        owner = SILVER;
                    }
                    System.out.println(xpos + " " + ypos);
                    boardSquares[xpos][ypos].setPiece(new Piece(rank, owner));
                }
            }
            jf.repaint();
        }
    }
}
